﻿using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
using BGCMS.Models;

namespace BGCMS.Controllers
{
    /// <summary>
    /// HomeController takes care of displaying lists of content and static pages for all users
    /// (authorized or not). Additionally it contains methods accessable only to Administrator
    /// users that deal with his control over authorized users on the site
    /// (Banning them, making them administrators), and finally displays the admin welcome page
    /// </summary>
    [HandleError]
    public class HomeController : Controller
    {
        #region Variables

        BGRepository rep = null;

        #endregion Variables

        #region Constructor

        public HomeController()
        {
            rep = new BGRepository();
        }

        #endregion Constructor

        #region Display lists of Articles&Keywords

        /// <summary>
        /// Displays the frontpage of the site, containing keywords belonging to the frontpage
        /// MenuItem and lists of 10 most recent articles and 10 most read articles
        /// </summary>
        /// <returns>Get: / (index method)</returns>
        public ActionResult Index()
        {
            // Keywords with MenuItemID 11("Forsíða") should appear on the front page:
            var keywords = from k in rep.GetKeywords()
                           where k.MenuItemID == 11
                           select k;

            var newArticles = (from a in rep.GetAllArticles()
                               where a.IsPublished == true
                               orderby a.DateCreated descending
                               select a).Take(10);

            var mostRead = (from b in rep.GetAllArticles()
                            where b.IsPublished == true
                            orderby b.ReadCount descending
                            select b).Take(10);

            HomeViewModel model = new HomeViewModel
            {
                Keywords = keywords,
                NewArticles = newArticles,
                PopularArticles = mostRead,
            };

            return View(model);
        }

        /// <summary>
        /// Displays a list of 10 most read and 10 most recent articles belonging to a given
        /// MenuItem ID. Also displays a list of keywords belonging to that MenuItem
        /// </summary>
        /// <param name="id">The ID of the MenuItem</param>
        /// <returns>Get: /Home/Menu/[id]</returns>
        public ActionResult Menu(int? id)
        {
            // Only valid id's are 1-5 and 6-9:
            if (id.HasValue && id.Value > 0 && id.Value != 6 && id.Value < 10)
            {
                var keywords = from k in rep.GetKeywords()
                               where k.MenuItemID == id.Value
                               select k;

                // Fetch all articles corresponding to the given MenuItem
                var articles = from a in rep.GetAllArticles()
                               from k in rep.GetKeywordsInArticle()
                               from m in keywords
                               where a.ID == k.ArticleID
                               && k.KeywordID == m.ID
                               && m.MenuItemID == id.Value
                               select a;

                var newArticles = (from a in articles
                                   where a.IsPublished == true
                                   orderby a.DateCreated descending
                                   select a).Take(10);

                var mostRead = (from b in articles
                                where b.IsPublished == true
                                orderby b.ReadCount descending
                                select b).Take(10);

                HomeViewModel model = new HomeViewModel { Keywords = keywords, NewArticles = newArticles, PopularArticles = mostRead };

                return View("Index", model);
            }

            return View("NotFound");
        }

        /// <summary>
        /// Displays a list of articles connected to a particular keyword
        /// </summary>
        /// <param name="id">The ID of the Keyword</param>
        /// <returns>Get: /Home/ListOfArticlesByKeyword/[id]</returns>
        public ActionResult ListOfArticlesByKeyword(int? id)
        {
            if (id.HasValue)
            {
                var keyword = (from k in rep.GetKeywords()
                               where k.ID == id.Value
                               select k).SingleOrDefault();

                if (keyword == null)
                {
                    return View("NotFound");
                }

                var articles = from a in rep.GetAllArticles()
                               from k in rep.GetKeywordsInArticle()
                               where k.ArticleID == a.ID
                               && k.KeywordID == id.Value
                               && a.IsPublished == true
                               select a;

                // Fetch other keywords that belong to the same MenuItem as the keyword in question
                var others = from o in rep.GetKeywords()
                             where o.MenuItemID == keyword.MenuItemID
                             select o;

                ArticleListViewModel model = new ArticleListViewModel
                {
                    Articles = articles,
                    Keyword = keyword,
                    OtherKeywords = others
                };

                return View(model);
            }
            return View("NotFound");
        }

        #endregion

        #region Static Pages

        /// <summary>
        /// Displays the terms of use displayed during user registration
        /// </summary>
        /// <returns>Get: /Home/UserTerms</returns>
        public ActionResult UserTerms()
        {
            return View();
        }

        /// <summary>
        /// Displays information about the founders of the organization
        /// </summary>
        /// <returns>Get: /Home/About</returns>
        public ActionResult About()
        {
            return View();
        }

        /// <summary>
        /// Displays information on how to donate money to the organization
        /// </summary>
        /// <returns>Get: /Home/Donate</returns>
        public ActionResult Donate()
        {
            return View();
        }

        /// <summary>
        /// Displays information on breastfeeding for foreigners
        /// </summary>
        /// <returns>Get: /Home/Foreigners</returns>
        public ActionResult Foreigners()
        {
            return View();
        }

        /// <summary>
        /// Displays a list of videos reccommended by the organization
        /// </summary>
        /// <returns>Get: /Home/Video</returns>
        public ActionResult Video()
        {
            return View();
        }

        /// <summary>
        /// Displays a list of links reccommended by the organization
        /// </summary>
        /// <returns>Get: /Home/Links</returns>
        public ActionResult Links()
        {
            return View();
        }

        /// <summary>
        /// Displays a graph users can input information about a child to see
        /// how it corresponds to WHO standards
        /// </summary>
        /// <returns>Get: /Home/Graph</returns>
        public ActionResult Graph()
        {
            return View();
        }

        #endregion

        #region Administrator methods

        /// <summary>
        /// Only accessible to Administrator users, displays the main welcome page of the sites "BackEnd"
        /// Contains links to all the main functions an administrator can do and the number of
        /// pending Articles, Questions and Specialist applications.
        /// </summary>
        /// <returns>Get: /Home/Admin</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult Admin()
        {
            var articles = from a in rep.GetAllArticles()
                           where a.IsPublished == false
                           && a.IsApproved == false
                           select a;

            var specialists = from s in rep.GetAllSpecialist()
                              where s.IsApproved == false
                              select s;

            var questions = from q in rep.GetAllQuestions()
                            where q.IsAnswered == false
                            select q;

            AdminViewModel model = new AdminViewModel
            {
                ArticleCount = articles.Count(),
                SpecialistCount = specialists.Count(),
                QuestionCount = questions.Count()
            };

            return View(model);
        }

        /// <summary>
        /// Displays a list of active users and an option to ban them
        /// Only accessible to Administrator users
        /// </summary>
        /// <returns>Get: /Home/DisplayUsers</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult DisplayUsers()
        {
            var users = from u in rep.GetActiveUsers()
                        orderby u.UserName descending
                        select u;

            return View(users);
        }

        /// <summary>
        /// Displays a list of banned users and an option to restore them
        /// Only accessible to Administator users
        /// </summary>
        /// <returns>Get: /Home/BannedUsers</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult BannedUsers()
        {
            var users = from u in rep.GetBannedUsers()
                        orderby u.UserName descending
                        select u;

            return View(users);
        }

        /// <summary>
        /// Bans a particular user from logging onto the site.
        /// Only accessible to Administrator users
        /// </summary>
        /// <param name="id">The ID of the user to be banned</param>
        /// <returns>Json Data indicating success</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult Banned(Guid? id)
        {
            if (id.HasValue)
            {
                // Find the user:
                MembershipUser u = Membership.GetUser(id.Value);
                if (u == null)
                {
                    return View("UserNotFound");
                }

                // Ban the user:
                u.IsApproved = false;
                Membership.UpdateUser(u);

                return Json("Success");
            }

            return View("Error");
        }

        /// <summary>
        /// Approves a previously banned user
        /// </summary>
        /// <param name="id">The ID of the user to be approved</param>
        /// <returns>Json data indicating success</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult Approved(Guid? id)
        {
            if (id.HasValue)
            {
                // Find the user:
                MembershipUser u = Membership.GetUser(id.Value);
                if (u == null)
                {
                    return View("UserNotFound");
                }

                // Approve the user:
                u.IsApproved = true;
                Membership.UpdateUser(u);

                return Json("Success");
            }
            return View("Error");
        }

        /// <summary>
        /// Displays a list of Administrators with an option to remove them from the Administrators role
        /// and a list of approved Specialists that are not Administrators with an option to make them Administrators
        /// </summary>
        /// <returns>Get: /Home/Administrators</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult Administrators()
        {
            // Fetching a collection of specialists in Administrators role:
            var admins = from a in rep.GetAllSpecialist()
                         // Fetching a collection of aspnet_user objects to compare userdId's with Specialists
                         from u in
                             (from r in rep.GetAllUsers()
                              where Roles.IsUserInRole(r.UserName, "Administrators")    // Method only accepts username as parameter
                              select r)
                         where a.UserID == u.UserId
                         select a;

            // Fetching a collection of specialists not in Administrators role
            // i.e. not in the previous collection above
            var specialists = (from s in rep.GetAllSpecialist()
                               where !(from a in admins
                                       select a.UserID).Contains(s.UserID)
                               select s);

            AdministratorsViewModel model = new AdministratorsViewModel { Admins = admins, Others = specialists };

            return View(model);
        }

        /// <summary>
        /// Adds a user, that needs to be an approved Specialist, to the Administrators role.
        /// Only accessible to Administrators
        /// </summary>
        /// <param name="id">The ID of the Specialist/User to be made Administrator</param>
        /// <returns>Get: /Home/Administrators</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult MakeAdmin(Guid? id)
        {
            if (id.HasValue)
            {
                //Find the user:
                MembershipUser user = Membership.GetUser(id.Value);

                if (user == null)
                {
                    return View("NotFound");
                }

                // Prevents a user that is not also a Specialist from being added to the Administrators group:
                if (!Roles.IsUserInRole(user.UserName, "Specialists"))
                {
                    return View("MakeAdminError");
                }

                // Add user to role:
                Roles.AddUserToRole(user.UserName, "Administrators");
                Membership.UpdateUser(user);

                return RedirectToAction("Administrators");
            }
            return View("Error");
        }

        /// <summary>
        /// Removes a user from the Administrators role.
        /// Only accessible to other Administrators (user cannot remove himself)
        /// </summary>
        /// <param name="id">The user ID of the Administrator to be removed</param>
        /// <returns>Get: /Home/Administrators</returns>
        [Authorize(Roles = "Administrators")]
        public ActionResult RemoveAdmin(Guid? id)
        {
            if (id.HasValue)
            {
                MembershipUser user = Membership.GetUser(id.Value);
                if (user == null)
                {
                    return View("Error");
                }

                // Prevents a user from removing himself from the Administrators group:
                if (user.UserName == User.Identity.Name)
                {
                    return View("AdminError");
                }

                // Remove user from role:
                Roles.RemoveUserFromRole(user.UserName, "Administrators");
                Membership.UpdateUser(user);

                return RedirectToAction("Administrators");
            }
            return View("NotFound");
        }

        #endregion
    }
}